/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.d.lib.common.widget.toggle;

import com.d.lib.common.widget.AnimatorValueUtils;
import com.d.lib.common.widget.AttrUtils;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.render.BlurDrawLooper;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.utils.Color;
import ohos.agp.utils.Rect;
import ohos.agp.utils.RectFloat;
import ohos.app.Context;
import ohos.multimodalinput.event.TouchEvent;

public class ToggleButton extends Component implements ToggleView, Component.DrawTask,
    Component.EstimateSizeListener, Component.TouchEventListener {
  private int mWidth;
  private int mHeight;

  private Rect mRect;
  private RectFloat mRectF;
  private Paint mPaintThumb;
  private Paint mPaintTrack;
  private Paint mPaintPadding;
  private Paint mPaintShader;

  private int mTouchSlop;
  private boolean mIsOpen;
  private boolean mIsClickValid;

  private float mPadding;
  private int mDuration;

  private int mColorThumb, mColorTrackOpen, mColorTrackOff, mColorPadding;
  private float mDX, mDY;
  private AnimatorValue mAnimation;
  private float mFactor = 1; // 0-1
  private OnToggleListener mListener;

  public ToggleButton(Context context) {
    this(context, null);
  }

  public ToggleButton(Context context, AttrSet attrs) {
    this(context, attrs, 0);
  }

  public ToggleButton(Context context, AttrSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    initTypedArray(context, attrs);
    init(context);
  }

  /**
   * 设置外圆线颜色
   */
  public void setmColorPadding(int padding) {
    mColorPadding = padding;
  }

  /**
   * 设置外圆线宽度
   */
  public void setmPadding(int padding) {
    mPadding = padding;
  }

  /**
   * 设置按钮颜色
   */
  public void setmColorThumb(int thumb) {
    mColorThumb = thumb;
  }

  /**
   * 设置打开颜色
   */
  public void setmColorTrackOpen(int color) {
    mColorTrackOpen = color;
  }

  /**
   * 设置关闭颜色
   */
  public void setmColorTrackOff(int color) {
    mColorTrackOff = color;
  }

  /**
   * 设置动画时长
   */
  public void setmDuration(int duration) {
    mDuration = duration;
  }

  private void initTypedArray(Context context, AttrSet attrs) {

    mColorThumb = AttrUtils.getColorFromAttr(attrs, "lib_pub_tbtn_colorThumb",
        Color.WHITE.getValue());
    mColorTrackOpen = AttrUtils.getColorFromAttr(attrs, "lib_pub_tbtn_colorTrackOpen",
        Color.getIntColor("#008577"));
    mColorTrackOff = AttrUtils.getColorFromAttr(attrs, "lib_pub_tbtn_colorTrackOff",
        Color.WHITE.getValue());
    mColorPadding = AttrUtils.getColorFromAttr(attrs, "lib_pub_tbtn_colorPadding",
        Color.getIntColor("#e3e4e5"));
    mPadding = AttrUtils.getDimensionFromAttr(attrs, "lib_pub_tbtn_padding", 1);
    mDuration = AttrUtils.getIntFromAttr(attrs, "lib_pub_tbtn_duration", 0);
  }

  private void init(Context context) {
    mTouchSlop = 24;

    mRect = new Rect();
    mRectF = new RectFloat();

    mPaintThumb = new Paint();
    mPaintThumb.setColor(new Color(mColorThumb));

    mPaintTrack = new Paint();
    mPaintTrack.setColor(new Color(mColorTrackOff));

    mPaintPadding = new Paint();
    mPaintPadding.setColor(new Color(mColorPadding));

    mPaintShader = new Paint();
    mPaintShader.setColor(new Color(mColorThumb));
    mPaintShader.setAntiAlias(true);
    mPaintShader.setBlurDrawLooper(new BlurDrawLooper(3, 3, 0, new Color(Color.getIntColor(
        "#eeeeee"))));

    mAnimation = new AnimatorValue();
    mAnimation.setDuration(mDuration);
    mAnimation.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
      @Override
      public void onUpdate(AnimatorValue animatorValue, float v) {
        mFactor = AnimatorValueUtils.getAnimatedValue(v, 0f, 1f);
        invalidate();
      }
    });
    addDrawTask(this::onDraw);
    setEstimateSizeListener(this::onEstimateSize);
    setTouchEventListener(this::onTouchEvent);
  }

  @Override
  public void onDraw(Component component, Canvas canvas) {
    float rectRadius = mHeight / 2f;
    mRect.set(0, 0, mWidth, mHeight);
    mRectF.modify(mRect);
    canvas.drawRoundRect(mRectF, rectRadius, rectRadius,
        mIsOpen ? mPaintTrack : mPaintPadding);

    mRect.set((int) mPadding, (int) mPadding,
        (int) (mWidth - mPadding), (int) (mHeight - mPadding));
    mRectF.modify(mRect);
    canvas.drawRoundRect(mRectF, rectRadius, rectRadius, mPaintTrack);

    float c0 = mHeight / 2f;
    float c1 = mWidth - mHeight / 2f;
    float start = !mIsOpen ? c1 : c0;
    float end = mIsOpen ? c1 : c0;
    float offsetX = start + (end - start) * mFactor;

    canvas.drawCircle(offsetX, mHeight / 2f, mHeight / 2f - mPadding, mPaintShader);
  }

  @Override
  public boolean onEstimateSize(int widthMeasureSpec, int heightMeasureSpec) {
    mWidth = EstimateSpec.getSize(widthMeasureSpec);
    mHeight = EstimateSpec.getSize(heightMeasureSpec);
    setEstimatedSize(EstimateSpec.getSizeWithMode(mWidth, EstimateSpec.PRECISE),
        EstimateSpec.getSizeWithMode(mHeight, EstimateSpec.PRECISE));
    return true;
  }

  @Override
  public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
    if (!(mFactor == 0 || mFactor == 1)) {
      return false;
    }
    float eX = touchEvent.getPointerPosition(0).getX();
    float eY = touchEvent.getPointerPosition(0).getY();

    switch (touchEvent.getAction()) {
      case TouchEvent.PRIMARY_POINT_DOWN:
        mDX = eX;
        mDY = eY;
        mIsClickValid = true;

      case TouchEvent.POINT_MOVE:
        if (mIsClickValid && (Math.abs(eX - mDX) > mTouchSlop || Math.abs(eY - mDY) > mTouchSlop)) {
          mIsClickValid = false;
        }
        return mIsClickValid;

      case TouchEvent.CANCEL:
      case TouchEvent.PRIMARY_POINT_UP:
        if (!mIsClickValid || eX < 0 || eX > mWidth || eY < 0 || eY > mHeight) {
          return false;
        }
        toggle();
        return true;
    }
    return true;
  }

  /**
   * Start animation
   */
  public void start() {
    stop();
    if (mAnimation != null) {
      mAnimation.start();
    }
  }

  /**
   * Stop animation
   */
  public void stop() {
    if (mAnimation != null) {
      mAnimation.cancel();
    }
  }

  @Override
  public void toggle() {
    mIsOpen = !mIsOpen;
    if (mIsOpen) {
      mPaintTrack.setColor(new Color(mColorTrackOpen));
    } else {
      mPaintTrack.setColor(new Color(mColorTrackOff));
    }
    if (mDuration <= 0) {
      mFactor = 1f;
      invalidate();
    } else {
      start();
    }
    if (mListener != null) {
      mListener.onToggle(mIsOpen);
    }
  }

  @Override
  public boolean isOpen() {
    return mIsOpen;
  }

  @Override
  public void setOpen(boolean open) {
    if (mFactor != 0 && mFactor != 1) {
      return;
    }
    stop();
    mIsOpen = open;
    mFactor = 1f;
    if (mIsOpen) {
      mPaintTrack.setColor(new Color(mColorTrackOpen));
    } else {
      mPaintTrack.setColor(new Color(mColorTrackOff));
    }
    invalidate();
  }

  @Override
  public void setOnToggleListener(OnToggleListener l) {
    this.mListener = l;
  }
}